--- /dev/null
+#include "config.h"
+
+#include "gskvulkanclipprivate.h"
+
+#include "gskroundedrectprivate.h"
+
+void
+gsk_vulkan_clip_init_empty (GskVulkanClip *clip,
+ const graphene_rect_t *rect)
+{
+ clip->type = GSK_VULKAN_CLIP_NONE;
+ gsk_rounded_rect_init_from_rect (&clip->rect, rect, 0);
+}
+
+static void
+gsk_vulkan_clip_init_copy (GskVulkanClip *self,
+ const GskVulkanClip *src)
+{
+ self->type = src->type;
+ gsk_rounded_rect_init_copy (&self->rect, &src->rect);
+}
+
+gboolean
+gsk_vulkan_clip_intersect_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const graphene_rect_t *rect)
+{
+ if (graphene_rect_contains_rect (rect, &src->rect.bounds))
+ {
+ gsk_vulkan_clip_init_copy (dest, src);
+ return TRUE;
+ }
+ if (!graphene_rect_intersection (rect, &src->rect.bounds, NULL))
+ {
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ return TRUE;
+ }
+
+ switch (src->type)
+ {
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_NONE:
+ gsk_vulkan_clip_init_copy (dest, src);
+ if (graphene_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds))
+ dest->type = GSK_VULKAN_CLIP_RECT;
+ else
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_RECT:
+ gsk_vulkan_clip_init_copy (dest, src);
+ if (!graphene_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds))
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ if (gsk_rounded_rect_contains_rect (&src->rect, rect))
+ {
+ dest->type = GSK_VULKAN_CLIP_RECT;
+ gsk_rounded_rect_init_from_rect (&dest->rect, rect, 0);
+ }
+ else
+ {
+ /* some points of rect are inside src's rounded rect,
+ * some are outside. */
+ /* XXX: If the 2 rects don't intersect on rounded corners,
+ * we could actually compute a new clip here.
+ */
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsk_vulkan_clip_intersect_rounded_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const GskRoundedRect *rounded)
+{
+ if (gsk_rounded_rect_contains_rect (rounded, &src->rect.bounds))
+ {
+ gsk_vulkan_clip_init_copy (dest, src);
+ return TRUE;
+ }
+ if (!graphene_rect_intersection (&rounded->bounds, &src->rect.bounds, NULL))
+ {
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ return TRUE;
+ }
+
+ switch (src->type)
+ {
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED;
+ break;
+
+ case GSK_VULKAN_CLIP_NONE:
+ dest->type = gsk_rounded_rect_is_circular (&dest->rect) ? GSK_VULKAN_CLIP_ROUNDED_CIRCULAR : GSK_VULKAN_CLIP_ROUNDED;
+ gsk_rounded_rect_init_copy (&dest->rect, rounded);
+ break;
+
+ case GSK_VULKAN_CLIP_RECT:
+ if (graphene_rect_contains_rect (&src->rect.bounds, &rounded->bounds))
+ {
+ dest->type = gsk_rounded_rect_is_circular (&dest->rect) ? GSK_VULKAN_CLIP_ROUNDED_CIRCULAR : GSK_VULKAN_CLIP_ROUNDED;
+ gsk_rounded_rect_init_copy (&dest->rect, rounded);
+ return TRUE;
+ }
+ /* some points of rect are inside src's rounded rect,
+ * some are outside. */
+ /* XXX: If the 2 rects don't intersect on rounded corners,
+ * we could actually compute a new clip here.
+ */
+ return FALSE;
+
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ /* XXX: improve */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gsk_vulkan_clip_contains_rect (const GskVulkanClip *self,
+ const graphene_rect_t *rect)
+{
+ switch (self->type)
+ {
+ default:
+ g_assert_not_reached();
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ return FALSE;
+
+ case GSK_VULKAN_CLIP_NONE:
+ return TRUE;
+
+ case GSK_VULKAN_CLIP_RECT:
+ return graphene_rect_contains_rect (&self->rect.bounds, rect);
+
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ return gsk_rounded_rect_contains_rect (&self->rect, rect);
+ }
+}
--- /dev/null
+#ifndef __GSK_VULKAN_CLIP_PRIVATE_H__
+#define __GSK_VULKAN_CLIP_PRIVATE_H__
+
+#include <gdk/gdk.h>
+#include <graphene.h>
+#include <gsk/gskroundedrect.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ /* The whole area is clipped, no drawing is necessary.
+ * This can't be handled by return values because for return
+ * values we return if clips could even be computed.
+ */
+ GSK_VULKAN_CLIP_ALL_CLIPPED,
+ /* No clipping is necesary, but the clip rect is set
+ * to the actual bounds of the underlying framebuffer
+ */
+ GSK_VULKAN_CLIP_NONE,
+ /* The clip is a rectangular area */
+ GSK_VULKAN_CLIP_RECT,
+ /* The clip is a rounded rectangle, and for every corner
+ * corner.width == corner.height is true
+ */
+ GSK_VULKAN_CLIP_ROUNDED_CIRCULAR,
+ /* The clip is a rounded rectangle */
+ GSK_VULKAN_CLIP_ROUNDED
+} GskVulkanClipComplexity;
+
+typedef struct _GskVulkanClip GskVulkanClip;
+
+struct _GskVulkanClip
+{
+ GskVulkanClipComplexity type;
+ GskRoundedRect rect;
+};
+
+void gsk_vulkan_clip_init_empty (GskVulkanClip *clip,
+ const graphene_rect_t *rect);
+
+gboolean gsk_vulkan_clip_intersect_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT;
+gboolean gsk_vulkan_clip_intersect_rounded_rect (GskVulkanClip *dest,
+ const GskVulkanClip *src,
+ const GskRoundedRect *rounded) G_GNUC_WARN_UNUSED_RESULT;
+
+gboolean gsk_vulkan_clip_contains_rect (const GskVulkanClip *self,
+ const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
+
+#endif /* __GSK_VULKAN_CLIP_PRIVATE_H__ */
#include "gskvulkanrenderpassprivate.h"
+#include "gskrendernodeprivate.h"
+#include "gskrenderer.h"
+#include "gskroundedrectprivate.h"
#include "gskvulkanblendpipelineprivate.h"
+#include "gskvulkanclipprivate.h"
#include "gskvulkancolorpipelineprivate.h"
#include "gskvulkanimageprivate.h"
-#include "gskrendernodeprivate.h"
-#include "gskrenderer.h"
#include "gskvulkanpushconstantsprivate.h"
#include "gskvulkanrendererprivate.h"
typedef enum {
/* GskVulkanOpRender */
GSK_VULKAN_OP_FALLBACK,
+ GSK_VULKAN_OP_FALLBACK_CLIP,
+ GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP,
GSK_VULKAN_OP_SURFACE,
GSK_VULKAN_OP_TEXTURE,
GSK_VULKAN_OP_COLOR,
GskVulkanOpType type;
GskRenderNode *node; /* node that's the source of this op */
GskVulkanPipeline *pipeline; /* pipeline to use */
+ GskRoundedRect clip; /* clip rect (or random memory if not relevant) */
GskVulkanImage *source; /* source image to render */
gsize vertex_offset; /* offset into vertex buffer */
gsize vertex_count; /* number of vertices */
gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanPushConstants *constants,
+ const GskVulkanClip *clip,
GskRenderNode *node)
{
GskVulkanOp op = {
{
case GSK_NOT_A_RENDER_NODE:
g_assert_not_reached ();
- break;
-
+ return;
default:
- op.type = GSK_VULKAN_OP_FALLBACK;
- op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT);
- g_array_append_val (self->render_ops, op);
- break;
+ goto fallback;
case GSK_CAIRO_NODE:
- if (gsk_cairo_node_get_surface (node) != NULL)
- {
- op.type = GSK_VULKAN_OP_SURFACE;
- op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT);
- g_array_append_val (self->render_ops, op);
- }
- break;
+ if (gsk_cairo_node_get_surface (node) == NULL)
+ return;
+ if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds))
+ goto fallback;
+ op.type = GSK_VULKAN_OP_SURFACE;
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT);
+ g_array_append_val (self->render_ops, op);
+ return;
case GSK_TEXTURE_NODE:
+ if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds))
+ goto fallback;
op.type = GSK_VULKAN_OP_TEXTURE;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT);
g_array_append_val (self->render_ops, op);
- break;
+ return;
case GSK_COLOR_NODE:
+ if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds))
+ goto fallback;
op.type = GSK_VULKAN_OP_COLOR;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_COLOR);
g_array_append_val (self->render_ops, op);
- break;
+ return;
case GSK_CONTAINER_NODE:
{
for (i = 0; i < gsk_container_node_get_n_children (node); i++)
{
- gsk_vulkan_render_pass_add_node (self, render, constants, gsk_container_node_get_child (node, i));
+ gsk_vulkan_render_pass_add_node (self, render, constants, clip, gsk_container_node_get_child (node, i));
}
}
- break;
+ return;
+
case GSK_TRANSFORM_NODE:
{
graphene_matrix_t transform;
+ GskVulkanClip new_clip;
+ graphene_rect_t rect;
+
+ if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds))
+ goto fallback;
gsk_transform_node_get_transform (node, &transform);
op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS;
gsk_vulkan_push_constants_multiply_mvp (&op.constants.constants, &transform);
g_array_append_val (self->render_ops, op);
- gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_transform_node_get_child (node));
+ graphene_matrix_transform_bounds (&transform, &clip->rect.bounds, &rect);
+ gsk_vulkan_clip_init_empty (&new_clip, &rect);
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &new_clip, gsk_transform_node_get_child (node));
gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants);
g_array_append_val (self->render_ops, op);
}
- break;
+ return;
+
+ case GSK_CLIP_NODE:
+ {
+ GskVulkanClip new_clip;
+
+ if (!gsk_vulkan_clip_intersect_rect (&new_clip, clip, gsk_clip_node_peek_clip (node)))
+ goto fallback;
+ if (new_clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED)
+ return;
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &new_clip, gsk_clip_node_get_child (node));
+ }
+ return;
+
+ case GSK_ROUNDED_CLIP_NODE:
+ {
+ GskVulkanClip new_clip;
+ if (!gsk_vulkan_clip_intersect_rounded_rect (&new_clip, clip, gsk_rounded_clip_node_peek_clip (node)))
+ goto fallback;
+ if (new_clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED)
+ return;
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &new_clip, gsk_rounded_clip_node_get_child (node));
+ }
+ return;
+ }
+
+ g_assert_not_reached ();
+ return;
+
+fallback:
+ switch (clip->type)
+ {
+ case GSK_VULKAN_CLIP_NONE:
+ op.type = GSK_VULKAN_OP_FALLBACK;
+ break;
+ case GSK_VULKAN_CLIP_RECT:
+ op.type = GSK_VULKAN_OP_FALLBACK_CLIP;
+ gsk_rounded_rect_init_copy (&op.render.clip, &clip->rect);
+ break;
+ case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
+ case GSK_VULKAN_CLIP_ROUNDED:
+ op.type = GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP;
+ gsk_rounded_rect_init_copy (&op.render.clip, &clip->rect);
+ break;
+ case GSK_VULKAN_CLIP_ALL_CLIPPED:
+ default:
+ g_assert_not_reached ();
+ return;
}
+ op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT);
+ g_array_append_val (self->render_ops, op);
}
void
gsk_vulkan_render_pass_add (GskVulkanRenderPass *self,
GskVulkanRender *render,
const graphene_matrix_t *mvp,
+ const graphene_rect_t *viewport,
GskRenderNode *node)
{
GskVulkanOp op = { 0, };
+ GskVulkanClip clip;
op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS;
gsk_vulkan_push_constants_init (&op.constants.constants, mvp);
g_array_append_val (self->render_ops, op);
- gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, node);
+ gsk_vulkan_clip_init_empty (&clip, viewport);
+
+ gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &clip, node);
}
static void
node = op->node;
+ /* XXX: We could intersect bounds with clip bounds here */
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
ceil (node->bounds.size.width),
ceil (node->bounds.size.height));
cr = cairo_create (surface);
cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y);
+ if (op->type == GSK_VULKAN_OP_FALLBACK_CLIP)
+ {
+ cairo_rectangle (cr,
+ op->clip.bounds.origin.x, op->clip.bounds.origin.y,
+ op->clip.bounds.size.width, op->clip.bounds.size.height);
+ cairo_clip (cr);
+ }
+ else if (op->type == GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP)
+ {
+ gsk_rounded_rect_path (&op->clip, cr);
+ cairo_clip (cr);
+ }
+ else
+ {
+ g_assert (op->type == GSK_VULKAN_OP_FALLBACK);
+ }
+
gsk_render_node_draw (node, cr);
cairo_destroy (cr);
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
gsk_vulkan_render_pass_upload_fallback (self, &op->render, render, uploader);
break;
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_SURFACE:
case GSK_VULKAN_OP_TEXTURE:
op->render.vertex_count = gsk_vulkan_blend_pipeline_count_vertex_data (GSK_VULKAN_BLEND_PIPELINE (op->render.pipeline));
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_SURFACE:
case GSK_VULKAN_OP_TEXTURE:
{
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_SURFACE:
case GSK_VULKAN_OP_TEXTURE:
op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source);
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
+ case GSK_VULKAN_OP_FALLBACK_CLIP:
+ case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_SURFACE:
case GSK_VULKAN_OP_TEXTURE:
if (current_pipeline != op->render.pipeline)